/*
	Copyright (C) 2008 by Jean-FranÁois Geyelin
	jfgeyelin@gmail.com

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the
	Free Software Foundation, Inc.,
	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/


#include "globals.h"
#include "safeFileIO.h"
#include "StreamHandler.h"
#include "WaterSlime.h"
#include "Bouncer.h"
#include "Goomba.h"
#include "FallingPlateform.h"
#include "MysteriousBlock.h"
#include "InfoBlock.h"
#include "Snifit.h"
#include "Cannon1.h"
#include "Cannon2.h"
#include "BigChomp.h"
#include "PlateformTwoWay.h"
#include "OrbitingPlateform.h"
#include "TriggerCreateBlocks.h"
#include "CenterMap.h"
#include "HideTime.h"
#include "SetPlayerLives.h"
#include "SimonMaster.h"
#include "StillItem.h"
#include "SurfaceHandler.h"
#include "FloatingCoin.h"
#include "FindBuriedPlants.h"
#include "ShellMaker.h"
#include "RollingBallMaker.h"
#include "ShyTrainer.h"
#include "FracktailMaker.h"
#include "messages.h"


Map::Map(std::string fileName) {


    TEST_EXECUTION()
	objectList = new ObjectList();


    TEST_EXECUTION()


	FILE *f = fopen_safe(("maps/" + fileName).c_str(), "r");
    TEST_EXECUTION()
	if (f==NULL) {
	    LOG_ERROR("failed to read map\n");
        exit(0);
	}
	std::string str;

    TEST_EXECUTION()

	msg3("loading background...", 0);
	backGround = createPlane(f,false);

	TEST_EXECUTION()

	msg3("loading middleground...", 1);
	middleGround = createPlane(f,false);

	TEST_EXECUTION()



	msg3("loading foreground...", 2);
	foreGround = createPlane(f,true);

	TEST_EXECUTION()

	mapPixelWidth = foreGround->getWidth()*16;
	mapPixelHeight = foreGround->getHeight()*16;
	mapWidth = foreGround->getWidth();

	mapHeight = foreGround->getHeight();
	msg3("loading extra items...", 3);

	TEST_EXECUTION()

	loadItems(f);
	loadMusic(fileName);

	fclose (f);

	setCoefficients();

	objectArrayMap = (MyArray**)malloc((foreGround->getWidth())*(foreGround->getHeight())*sizeof(MyArray*));

	int i;
	for (i=0;i<(foreGround->getWidth())*(foreGround->getHeight());i++) {
        objectArrayMap[i] = new MyArray(1);
	}
	map = this;
	Object::setMap();
}


Map::~Map(void) {
	int i;
	TEST_EXECUTION()
	for (i=0;i<foreGround->getWidth()*foreGround->getHeight();i++) {
        delete objectArrayMap[i];
	}
	TEST_EXECUTION()
	free(objectArrayMap);

    TEST_EXECUTION()
	delete foreGround;  /** <= BUG  */
	TEST_EXECUTION()
	delete middleGround;
	TEST_EXECUTION()
	delete backGround;
	TEST_EXECUTION()
    delete objectList;

/*
    Player *p;

    int i;

        nCree = 0;
        nDel = 0;
objectList = new ObjectList();
    for (i=0;i<10;i++) {

        p = new Player(0,0);

    }
delete objectList;


	PRINTF("objets vraiment crÈes: %i objets vraiments del: %i\n",nCree,nDel);
	*/
}



Plane* Map::createPlane(FILE *f, bool mainPlane) {
	std::string str;
	if (StreamHandler::readString(f,&str)) {
		if (str=="null")
		return NULL;
		return new Plane(str,mainPlane);
	}
	return NULL;

}

void Map::loadItems(FILE *f) {


	std::string str;
	std::string str2;
	StreamHandler::readString(f, &str);

	FILE *f2 = fopen_safe(("maps/"+str).c_str(), "r");

	if (!f2) {
        LOG_ERROR("failed to load objetcs.\n")
        return;
	}

	long x,y;
	//float vx;
	str="";


	while (1) {
		if (!StreamHandler::readString(f2,&str)) {
			fclose(f2);

			return;
		}
		if (str.substr(0,2) != "//") {
			if (str=="Player") {
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				player = new Player(x,y);

				new WaterSlime(x+400,y+350+64);
			}

			if (str=="Bouncer") {
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				new Bouncer(x,y);
			}
			if (str=="Goomba") {
				std::string str;
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				StreamHandler::readString(f2,&str);
				new Goomba(x,y,str);
			}
			if (str=="FallingPlateform") {
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				new FallingPlateform(x,y);
            }


			if (str=="MysteriousBlock") {
				std::string str;
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				StreamHandler::readString(f2,&str);
				new MysteriousBlock(x,y,str);
			}

			if (str=="InfoBlock") {
				std::string str;
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				StreamHandler::readString(f2,&str);
				new InfoBlock(x,y,str);
			}
			if (str=="Snifit") {
				std::string str;
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				StreamHandler::readString(f2,&str);
				new Snifit(x,y,str);
			}
			if (str=="Cannon1") {
				std::string str;
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				StreamHandler::readString(f2,&str);
				new Cannon1(x,y,str);
   			}
			if (str=="Cannon2") {
				std::string str;
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				new Cannon1(x,y);
   			}
   			if (str=="BigChomp") {
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				new BigChomp(x,y);
			}
			if (str=="PlateformTwoWay") {
				float x1,y1,x2,y2,div;
				StreamHandler::readFloat(f2,&x1);
				StreamHandler::readFloat(f2,&y1);
				StreamHandler::readFloat(f2,&x2);
				StreamHandler::readFloat(f2,&y2);
				StreamHandler::readFloat(f2,&div);
				new PlateformTwoWay(x1,y1,x2,y2,div);
			}

			if (str=="PlateformTwoWay2") {
				float x1,y1,x2,y2,div;
				StreamHandler::readFloat(f2,&x1);
				StreamHandler::readFloat(f2,&y1);
				StreamHandler::readFloat(f2,&x2);
				StreamHandler::readFloat(f2,&y2);
				StreamHandler::readFloat(f2,&div);
				new PlateformTwoWay(x1*16,y1*16,x2*16,y2*16,div);
			}
			if (str=="OrbitingPlateform") {
				float radius;
				float angleSpeed;
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				StreamHandler::readFloat(f2,&radius);
				StreamHandler::readFloat(f2,&angleSpeed);
				new OrbitingPlateform(x,y,radius,angleSpeed);
			}
			if (str=="OrbitingPlateform2") {
				float radius;
				float angleSpeed;
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				StreamHandler::readFloat(f2,&radius);
				StreamHandler::readFloat(f2,&angleSpeed);
				new OrbitingPlateform(x*16,y*16,radius,angleSpeed);
			}
			if (str=="OrbitingPlateform3") {
				float radius;
				float angleSpeed;
				float angle;
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				StreamHandler::readFloat(f2,&radius);
				StreamHandler::readFloat(f2,&angleSpeed);
				StreamHandler::readFloat(f2,&angle);
				new OrbitingPlateform(x*16,y*16,radius,angleSpeed,angle);
			}
			if (str=="SimonMaster") {
                new SimonMaster();
			}
			if (str=="TriggerCreateBlocks") {
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				new TriggerCreateBlocks(x*16,y*16);
			}
			if (str=="Wario1") {
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
                new StillItem(x,y+16-76/2+2,"wario_training.png",2,72,76,40);
			}
			if (str=="StaticImage") {
				std::string str;
				long w;
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				StreamHandler::readLong(f2,&w);
				StreamHandler::readString(f2,&str);
                new StillItem(x,y,str,w);
			}


			if (str=="Cannon3") {
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
                new Cannon2(x,y-16);
			}

            if (str=="Coin") {
                StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
                new FloatingCoin(x,y);
            }

            if (str=="Donut") {
                StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
                new FallingPlateform(x,y);
            }

            if (str=="ShellMaker") {
                StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
                new ShellMaker(x,y);
            }
            if (str=="ShyTrainer") {
                StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				new ShyTrainer(x,y);
            }

			/* the following inherits from GameModifiers, not Object */
			if (str=="CenterMap") {
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
                new CenterMap(x,y);
			}

			if (str=="HideTime") {
                new HideTime();
			}

			if (str=="SetNumberOfLives") {
                StreamHandler::readLong(f2,&x);
                new SetPlayerLives(x);
			}

			if (str=="FindBuriedPlants") {
                new FindBuriedPlants();
			}

			if (str=="RollingBallMaker") {
			    new RollingBallMaker();
			}

			if (str=="FracktailMaker") {
                StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
                new FracktailMaker(x,y);
			}
			if (str=="MovingGround") {
				long w;
				StreamHandler::readLong(f2,&x);
				StreamHandler::readLong(f2,&y);
				StreamHandler::readLong(f2,&w);
				//new MovingGround(x,y,w);
			}




		}
	}

	fclose(f2);



}


void Map::loadMusic(std::string fileName) {
	std::string str;
	FILE *f = fopen_safe(("maps/music_list/music_"+fileName).c_str(), "r");
    if (!f) {
        LOG_ERROR("failed to load music list.\n")
        return;
	}
    StreamHandler::readString(f,&str);
    musicThread->smoothlyStartPlayingMusic("sound/"+str);
    fclose(f);
}

void Map::moveCamera(int x, int y) {
	camera->move(x,y);
}



void Map::setCoefficients() {

    //SCREEN_WIDTH+=2*BORDURE_WIDTH;

	#define gWidth(g) (((g->getWidth()-1)*g->getTileWidth())-SCREEN_WIDTH)
	#define gHeight(g) (((g->getHeight()-1)*g->getTileHeight())-SCREEN_HEIGHT)

	long w = gWidth(foreGround);
	long h = gHeight(foreGround);

	#define setXCoef(g) g->setXCoeff(((double)gWidth(g)/(double)w))
	#define setYCoef(g) g->setYCoeff(((double)gHeight(g)/(double)h))
    #define testAndSet(a) if (a) {setXCoef(a);setYCoef(a);}

    testAndSet(backGround);
    testAndSet(middleGround);
    testAndSet(foreGround);


	#undef testAndSet
	#undef gWidth
	#undef gHeight
	#undef setXCoeff
	#undef setYCoeff
}



void Map::draw() {
	int x = camera->getX();
	int y = camera->getY();
	positionAndDrawPlane(backGround,x,y);
	positionAndDrawPlane(middleGround,x,y);
	positionAndDrawPlane(foreGround,x,y);
}

void Map::positionAndDrawPlane(Plane *p, int x, int y) {
	if (p!=0){
		int w = (SCREEN_WIDTH/p->getTileWidth())+2;
		int h = (SCREEN_HEIGHT/p->getTileHeight())+3;
		x=(int)(x*p->getXCoeff());
		y=(int)(y*p->getYCoeff());
		p->draw(screenSurface,x,y,w,h);
	}
}




void Map::drawPreview(SDL_Surface *dest) {
	int W = SCREEN_WIDTH;
	int H = SCREEN_HEIGHT;

	SCREEN_WIDTH = PREVIEW_WIDTH1;
	SCREEN_HEIGHT = PREVIEW_HEIGHT1;


	camera = new Camera();
	for (int i=0;i<40;i++)
	map->moveCamera((int)player->getX(),(int)player->getY());

	int x = camera->getX();
	int y = camera->getY();

	Map::setCoefficients();


    GameModifier::executeAllModifications();
    GameModifier::deleteAllModifications();

	#define HEHE(p)\
	if (p!=0){\
		int w = (SCREEN_WIDTH/p->getTileWidth())+2;\
		int h = (SCREEN_HEIGHT/p->getTileHeight())+3;\
		p->draw(screenSurface,(int)(x*p->getXCoeff()),(int)(y*p->getYCoeff()),w,h);\
	}
	HEHE(backGround)
	HEHE(middleGround)
	HEHE(foreGround)

    objectList->animate();
    objectList->drawWithoutTheMovingObjects();

	#undef HEHE

    SurfaceHandler::SDL_BlitSurface(screenSurface,NULL,dest, NULL );


	delete camera;
	SCREEN_WIDTH = W;
	SCREEN_HEIGHT = H;
}



Plane* Map::getForeGround() {
	return foreGround;
}



MyArray** Map::getObjectArrayMap() {
	return objectArrayMap;
}

void Map::changeSaturation(void) {
    backGround->changeSaturation();
}

void Map::changeSaturation2(void) {
    backGround->changeSaturation2();
}
